package mspbots.qbd.online.core.profitandloss;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ObjectNode;
import lombok.extern.log4j.Log4j2;
import mspbots.qbd.common.QueryParams;
import mspbots.qbd.common.RabbitKeys;
import mspbots.qbd.common.ResultMsg;
import mspbots.qbd.common.ResultSync;
import mspbots.qbd.online.core.AbstractToolsUtil;
import mspbots.qbd.online.core.BaseSyncService;
import mspbots.qbd.online.oauth.TokenState;
import org.springframework.stereotype.Service;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.time.format.TextStyle;
import java.time.temporal.ChronoUnit;
import java.time.temporal.TemporalAdjusters;
import java.util.Locale;

/**
 * mspbots.data.qbd.ws.profitandloss.ProfitAndLossService
 *
 * @author Alex bob(https://github.com/vnobo)
 * @date Created by 2020/6/4
 */
@Log4j2
@Service
public class ProfitAndLossServiceImpl extends AbstractToolsUtil implements BaseSyncService {

    private Mono<JsonNode> get(LocalDate syncStartDate) {
        QueryParams params = QueryParams.withDefault("");
        params.add("start_date", syncStartDate.with(TemporalAdjusters.firstDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        params.add("end_date", syncStartDate.with(TemporalAdjusters.lastDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        return this.onlineClient.get("/company/{realmId}/reports/ProfitAndLoss", params);
    }

    private void sync(TokenState tokenState, LocalDate syncStartDate) {
        ResultSync resultSync = ResultSync.withDefault("PROFIT-AND-LOSS-STANDARD-SYNC");
        this.get(syncStartDate)
                .subscriberContext(ctx -> ctx.put("tokenState", tokenState))
                .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                .subscribe(result -> {
                    ObjectNode objectNode = result.deepCopy();
                    objectNode.put("tenantId", tokenState.getTenantId());
                    objectNode.put("timePeriod", syncStartDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) +
                            " " + syncStartDate.getYear());
                    this.sendEntity(RabbitKeys.PROFITANDLOSS_STANDARD_ONLINE_SYNC_QUEUE_KEY, ResultMsg.builder()
                            .requestId(resultSync.getRequestId()).id(syncStartDate.toEpochDay())
                            .action("none").entity(objectNode).tenantId(tokenState.getTenantId())
                            .syncDate(LocalDateTime.now()).build());
                }, err -> log.error("Sync ProfitAndLossDetail error, msg: {}", err.getMessage()));
    }

    private Mono<JsonNode> getDetail(LocalDate syncStartDate) {
        QueryParams params = QueryParams.withDefault("");
        params.add("start_date", syncStartDate.with(TemporalAdjusters.firstDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        params.add("end_date", syncStartDate.with(TemporalAdjusters.lastDayOfMonth())
                .format(DateTimeFormatter.ISO_DATE));
        return this.onlineClient.get("/company/{realmId}/reports/ProfitAndLossDetail", params);
    }

    private void syncDetail(TokenState tokenState, LocalDate syncStartDate) {
        ResultSync resultSync = ResultSync.withDefault("PROFIT-AND-LOSS-DETAIL-SYNC");
        this.getDetail(syncStartDate)
                .subscriberContext(ctx -> ctx.put("tokenState", tokenState))
                .subscriberContext(ctx -> ctx.put("resultSync", resultSync))
                .subscribe(result -> {
                    ObjectNode objectNode = result.deepCopy();
                    objectNode.put("tenantId", tokenState.getTenantId());
                    objectNode.put("timePeriod", syncStartDate.getMonth().getDisplayName(TextStyle.FULL, Locale.ENGLISH) +
                            " " + syncStartDate.getYear());
                    this.sendEntity(RabbitKeys.PROFITANDLOSS_DETAIL_ONLINE_SYNC_QUEUE_KEY, ResultMsg.builder()
                            .requestId(resultSync.getRequestId()).id(syncStartDate.toEpochDay())
                            .action("none").entity(objectNode).tenantId(tokenState.getTenantId())
                            .syncDate(LocalDateTime.now()).build());
                }, err -> log.error("Sync ProfitAndLossDetail error, msg: {}", err.getMessage()));
    }


    @Override
    public Mono<Void> syncData(TokenState tokenState) {
        LocalDate startDate = LocalDate.now().minusYears(10);
        long count = ChronoUnit.MONTHS.between(startDate, LocalDate.now());
        return Flux.range(0, Integer.parseInt(String.valueOf(count)))
                .map(startDate::plusMonths)
                .delayElements(Duration.ofSeconds(2))
                .doOnNext(sd -> this.sync(tokenState, sd))
                .doOnNext(sd -> this.syncDetail(tokenState, sd))
                .then();
    }
}
